/*
 * Copyright (C) 2014 naBerTech snc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.nabertech.example.android.wearable.sendphoto;

import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;

import com.example.android.wearable.sendphoto.R;
import com.nabertech.example.android.wearable.sendphoto.common.Constants;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
import com.google.android.gms.wearable.Asset;
import com.google.android.gms.wearable.DataApi.DataItemResult;
import com.google.android.gms.wearable.MessageApi.SendMessageResult;
import com.google.android.gms.wearable.MessageApi;
import com.google.android.gms.wearable.MessageEvent;
import com.google.android.gms.wearable.Node;
import com.google.android.gms.wearable.NodeApi;
import com.google.android.gms.wearable.PutDataMapRequest;
import com.google.android.gms.wearable.PutDataRequest;
import com.google.android.gms.wearable.Wearable;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;

/**
 * Allows user to start wearable activity and take a photo and send that as an asset to
 * the paired wearable.
 */
public class MainActivity extends Activity implements NodeApi.NodeListener,
        MessageApi.MessageListener, ConnectionCallbacks, OnConnectionFailedListener,
        MainModel.ModelObserver {

    private static final String TAG = "MainActivity";

    // Entry point for Google Play Services.
    private GoogleApiClient mGoogleApiClient;

    // True if camera is supported.
    private boolean mCameraSupported = false;

    // Views
    private Button mSendPhotoBtn;
    private ImageView mThumbView;
    private View mNotifyNewPhotoBtn;

    // Data Model.
    private MainModel mModel;

    // Image capture Request id.
    static final int REQUEST_IMAGE_CAPTURE = 1;

    @Override
    public void onCreate(Bundle b) {
        super.onCreate(b);
        setContentView(R.layout.main_activity);
        setupViews();

        mCameraSupported = getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA);

        mModel = new MainModel();
        mModel.setObserver(this);

        mModel.restore(b);

        // Create google API Client
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addApi(Wearable.API)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .build();
    }

    @Override
    protected void onSaveInstanceState (Bundle outState) {
        super.onSaveInstanceState(outState);
        mModel.commit(outState);
    }

    @Override
    protected void onStart() {
        super.onStart();
        mGoogleApiClient.connect();
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (mGoogleApiClient.isConnected()) {
            Wearable.MessageApi.removeListener(mGoogleApiClient, this);
            mGoogleApiClient.disconnect();
            mModel.setConnected(false);
        }
    }

    @Override //ConnectionCallbacks
    public void onConnected(Bundle connectionHint) {
        Log.d(TAG, "Google API Client was connected");
        mModel.setConnected(true);
        Wearable.NodeApi.addListener(mGoogleApiClient, this);
        Wearable.MessageApi.addListener(mGoogleApiClient, this);
    }

    @Override //ConnectionCallbacks
    public void onConnectionSuspended(int cause) {
        Log.d(TAG, "Connection to Google API client was suspended");
        mModel.setConnected(false);
        mSendPhotoBtn.setEnabled(false);
    }

    @Override //OnConnectionFailedListener
    public void onConnectionFailed(ConnectionResult result) {
        Log.e(TAG, "Connection to Google API client has failed");
        mModel.setConnected(false);
        Wearable.MessageApi.removeListener(mGoogleApiClient, this);
    }

    @Override //NodeListener
    public void onPeerConnected(final Node peer) {
        Log.d(TAG, "onPeerConnected: " + peer);
    }

    @Override //NodeListener
    public void onPeerDisconnected(final Node peer) {
        Log.d(TAG, "onPeerDisconnected: " + peer);
    }

    @Override //MessageListener
    public void onMessageReceived(final MessageEvent messageEvent) {
        Log.d(TAG, "onMessageReceived() A message from watch was received:" + messageEvent
                .getRequestId() + " " + messageEvent.getPath());

        // Check to see if the message is to start an activity or the reply to "I Like the photo"
        if (messageEvent.getPath().equals(Constants.SHOW_ACTIVITY_STARTED_PATH)) {
            mModel.setWaitingSendPhoto(true);
        } else if (messageEvent.getPath().equals(Constants.LIKE_PHOTO_PATH)) {
            // Clear bitmap image.
            mModel.setImageBitmap(null);

            switch (messageEvent.getData()[0]) {
                case Constants.LIKE:
                    mModel.setWaitingSendPhoto(false);
                    break;
                case Constants.DISLIKE:
                    // run camera activity
                    mModel.setWaitingSendPhoto(true);
                    dispatchTakePictureIntent();
                    break;
                default:
                    // Can't do anything
            }
        }
    }

    /**
     * Send a message to notify that a new photo is waiting to send.
     */
    public void onNotifyNewPhotoClick(View view) {
        Log.d(TAG, "Notify new photo");
        new NotifyNewPhotoTask().execute();
    }

    /**
     * AsyncTask that will query for a list of connected nodes and send a
     * "notify-new-photo" message to each connected node.
     */
    private class NotifyNewPhotoTask extends AsyncTask<Void, Void, Void> {

        @Override
        protected Void doInBackground(Void... args) {
            sendNotifyNewPhotoMessages();
            return null;
        }

        private void sendNotifyNewPhotoMessages() {
            Collection<String> nodes = getNodes();
            for (String node : nodes) {
                sendNotifyNewPhotoMessage(node);
            }
        }
    }

    /**
     * Get the list of connected Nodes.
     */
    private Collection<String> getNodes() {
        HashSet<String> results = new HashSet<String>();
        NodeApi.GetConnectedNodesResult nodes =
                Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await();

        for (Node node : nodes.getNodes()) {
            results.add(node.getId());
        }

        return results;
    }

    private void sendNotifyNewPhotoMessage(String node) {
        Log.d(TAG, "Send start activity message to: " + node);
        Wearable.MessageApi.sendMessage(
                mGoogleApiClient, node,
                Constants.NOTIFY_NEW_PHOTO, new byte[0]).setResultCallback(
                new ResultCallback<SendMessageResult>() {
                    @Override
                    public void onResult(SendMessageResult sendMessageResult) {
                        if (!sendMessageResult.getStatus().isSuccess()) {
                            Log.e(TAG, "Failed to send message with status code: "
                                    + sendMessageResult.getStatus().getStatusCode());
                        }
                    }
                }
        );
    }

    public void onTakePhotoClick(View view) {
        dispatchTakePictureIntent();
    }

    /**
     * Dispatches an {@link android.content.Intent} to take a photo.
     * Result will be returned back
     * in onActivityResult().
     */
    private void dispatchTakePictureIntent() {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
            startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
        }
    }

    /**
     * Callback from the launched activity.
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // Check if callback from the REQUEST_IMAGE_CAPTURE
        if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
            Bundle extras = data.getExtras();
            mModel.setImageBitmap((Bitmap) extras.get("data"));
        }
    }

    public void onSendPhotoClick(View view) {
        sendPhoto(toAsset(mModel.getImageBitmap()));
    }

    /**
     * Builds an {@link com.google.android.gms.wearable.Asset} from a bitmap. The image that we get
     * back from the camera in "data" is a thumbnail size. Typically, your image should not exceed
     * 320x320 and if you want to have zoom and parallax effect in your app, limit the size of your
     * image to 640x400. Resize your image before transferring to your wearable device.
     */
    private static Asset toAsset(Bitmap bitmap) {
        ByteArrayOutputStream byteStream = null;
        try {
            byteStream = new ByteArrayOutputStream();
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteStream);
            return Asset.createFromBytes(byteStream.toByteArray());
        } finally {
            if (null != byteStream) {
                try {
                    byteStream.close();
                } catch (IOException e) {
                    // ignore
                }
            }
        }
    }

    /**
     * Sends the asset that was created form the photo we took by adding it to the Data Item store.
     */
    private void sendPhoto(Asset asset) {
        PutDataMapRequest dataMapRequest = PutDataMapRequest.create(Constants.IMAGE_PATH);
        dataMapRequest.getDataMap().putAsset(Constants.IMAGE_KEY, asset);
        PutDataRequest request = dataMapRequest.asPutDataRequest();
        Wearable.DataApi.putDataItem(mGoogleApiClient, request)
                .setResultCallback(new ResultCallback<DataItemResult>() {
                    @Override
                    public void onResult(DataItemResult dataItemResult) {
                        Log.d(TAG, "Sending image was successful: " + dataItemResult.getStatus()
                                .isSuccess());
                    }
                });

    }

    /**
     * Sets up UI components.
     */
    private void setupViews() {
        mSendPhotoBtn = (Button) findViewById(R.id.sendPhoto);
        mThumbView = (ImageView) findViewById(R.id.imageView);
        mNotifyNewPhotoBtn = findViewById(R.id.start_wearable_activity);
    }

    @Override //ModelObserver
    public void ModelChanged() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mThumbView.setImageBitmap(mModel.getImageBitmap());
                mNotifyNewPhotoBtn.setEnabled(mModel.isConnected());
                mSendPhotoBtn.setEnabled(
                        mModel.isConnected() &&
                        mCameraSupported &&
                        mModel.isWaitingSendPhoto() &&
                        mModel.getImageBitmap() != null);
            }
        });
    }
}
